package DEEPERsource.DEEPERsource.source.machinelearning.kernel;

import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Random;
import java.util.Set;

import deeper.Interaction;

import edu.uci.ics.jung.graph.DirectedEdge;
import edu.uci.ics.jung.graph.Vertex;
import graph.Keys;

public class SyntacticKernel implements Kernel<Interaction>, Keys {
	
	//service variables to keep track of tree matching
	private int straight;
	private int reverse;
	//amount of length-1 paths (should be kept separately)
	private int amount;
	//length of longest matched subpath
	private int length;
	
	private double lambda = 1.0;
	//map holds the lengths and numbers of matched subtrees
	Map<Integer, Integer> lm;
	
	Map<String,Double> self = new HashMap<String,Double>();
	
	@SuppressWarnings("unchecked")
	public double compute(Interaction i1, Interaction i2){
		lm = new HashMap<Integer,Integer>();
		double result = 0;
		if(i1.depPath == null || i2.depPath == null){
			return result;
		}
		//double coef = lambda(i1.depPath.length)*lambda(i2.depPath.length);
		for(Vertex v1 : i1.depPath.vertices){
			for(Vertex v2 : i2.depPath.vertices){
				//straight = 0;
				//reverse = 0;
				//length = 0;
				//amount = 0; //keeps the number of one-step trees, matched for given vertices				
				//double subValue = Cm(v1,v2);
				//result += Math.pow(lambda, length)*subValue;
				result += Cm(v1,v2);
				//if(amount>0)
				//	addTreeCounts();
			}
		}
		//double norm = 1.0;
		/*if(!i1.id.equals(i2.id)){
			double self1, self2;
			if(!self.containsKey(i1.id))
				self.put(i1.id, compute(i1,i1));
			if(!self.containsKey(i2.id))
				self.put(i2.id, compute(i2,i2));
			self1 = self.get(i1.id);
			self2 = self.get(i2.id);			
			norm = self1*self2;//Math.sqrt(self1*self2);
		}*/
		//return result/(norm==0?1.0:norm);
		return result;
	}
	
	private int fact(int n){
		int result = 1;
		for(int i = 1; i<n+1; i++){
			result *= i;
		}
		return result;
	}
	
	private void addTreeCounts(){
		Integer val = lm.get(1);
		if(val==null){
			lm.put(new Integer(1), amount);
		}else{
			lm.put(new Integer(1), val+amount);
		}
		val = lm.get(2);
		if(val==null){
			lm.put(new Integer(2), fact(amount)/(2*fact(amount-2)));
		}else{
			lm.put(new Integer(2), val+fact(amount)/(2*fact(amount-2)));
		}
		if(length>2){
			val = lm.get(length);
			if(val==null){
				lm.put(new Integer(length), 1);
			}else{
				lm.put(new Integer(length), val+1);
			}
		}
	}
	
	public String getDistribution(){
		StringBuffer sb = new StringBuffer("");
		List<Integer> keys = new ArrayList<Integer>(lm.keySet());
		Collections.sort(keys);
		for(Integer key : keys){
			sb.append("\t"+key+" "+lm.get(key));
		}
		return sb.toString();
	}
	
	private double Cm(Vertex v1, Vertex v2){
		double result = 1;
		if(v1.getOutEdges().size()>0 && v2.getOutEdges().size()>0){
			for(Object e1 : v1.getOutEdges()){
				for(Object e2 : v2.getOutEdges()){
					if(((DirectedEdge)e1).getUserDatum(Keys.LABEL).equals(((DirectedEdge)e2).getUserDatum(Keys.LABEL))){
						result *= (Cm(((DirectedEdge)e1).getDest(),((DirectedEdge)e2).getDest())+2);
						//counting additional info for tree counts
						/*straight++;
						result *= (Cm(((DirectedEdge)e1).getDest(),((DirectedEdge)e2).getDest())+2);
						reverse++;
						if(straight==reverse){
							//this means, that we have traversed some branch of the tree and get back
							amount++;
							length +=straight;
							if(straight>1){
								Integer i = lm.get(straight);
								if(i==null){
									i = new Integer(0);
									lm.put(straight, 1);
								}else{
									lm.put(straight, i+1);
								}								
							}			
							straight = 0;
							reverse = 0;							
						}*/
					}
				}
			}
		}
		result--;
		return result;
	}
	
	private double lambda(int length){
		if(length<=4)
			return 1;
		Random r = new Random();
		if(length<=5)
			return (9-(length+r.nextDouble()))/5;
		return (13-(length+r.nextDouble()))/10;
	}
}
